﻿ /**
 * TraceListener.cs
 *  Listening server to process GPS trace signals
 *  (first point of entry for GPS broadcasts)
 *  
 *  11-MAY-2013 byasutake
 */

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Data.SqlClient;
using Newtonsoft.Json;
using System.Web.Script.Serialization;
using System.Collections.Generic;
using System.Threading;
using Microsoft.ComplexEventProcessing;
using Microsoft.ComplexEventProcessing.Linq;
using System.Data.Linq;
using System.Linq;
using System.Data.Spatial;
using Microsoft.AspNet.SignalR.Client.Hubs;
using System.Json;
using System.Net.WebSockets;





/**
 * Trace object holds fields for all data contained in each incoming GPS trace
 */
public class TestProgram
{

    //public static HttpListener listener = new HttpListener();

    private static IPAddress _dbServer;
    private static String _dbName;
    private static String _username = "ct";
    private static String _password = "ct";
    private static SqlConnection _criminalTrackerDB;

    // Constants
    private const String STREAMSERVER = "BRETT_WS2012";
    private const String STREAMAPP = "CriminalTracker";

    // JSON constants
    private const string OFFENDERS = "Offenders";

    private const string OFFENDER = "Offender";
    private const string OID = "OID";

    private const string LOCATION = "Location";
    private const string TIMESTAMP = "TimeStamp";
    private const string LAT = "Lat";
    private const string LONG = "Long"; 

    //public TestProgram()
    //{
    //    listener.Prefixes.Add("http://192.168.1.199:13000/");
    //    listener.Start();
    //    Console.WriteLine("Listening...");
    //    for (; ; )
    //    {
    //        HttpListenerContext ctx = listener.GetContext();
    //        new Thread(new testWorker(ctx).ProcessRequest).Start();
    //    }
    //}

    public static void Main()
    {

        //ServerR serverR = new ServerR();



        //string url = "http://localhost:13001/";
        //var server = new System.Net.WebSockets.s(url);

        //server.MapConnection<MyConnection>("/echo");

        //server.Start();

        //Console.WriteLine("Server running on {0}", url);

        //Console.ReadKey();

        //var hubConnection = new HubConnection("http://192.168.1.199:13001/"); //change ip and port
        //var serverHub = hubConnection.CreateHubProxy("ServerHub");
        //try
        //{
        //    hubConnection.Start().Wait();
        //    serverHub.Invoke("Subscribe");
        //}
        //catch
        //(AggregateException ar) { Console.WriteLine(ar.ToString()); }

        //serverHub.Invoke("Publish", "hello").Wait();

        //BEGIN WEBSOCKET
        
        





        //BEGIN TCP

        TcpListener sserver = null;
        try
        {
            // Set the TcpListener on port 13000.
            Int32 port = 13001;
            IPAddress localAddr = IPAddress.Parse("192.168.1.199");

            // TcpListener server = new TcpListener(port);
            sserver = new TcpListener(localAddr, port);

            // Start listening for client requests.
            sserver.Start();

            // Buffer for reading data
            Byte[] bytes = new Byte[256];
            String data = null;

            // Enter the listening loop. 
            while (true)
            {
                Console.Write("Waiting for a connection... ");

                // Perform a blocking call to accept requests. 
                // You could also user server.AcceptSocket() here.
                TcpClient client = sserver.AcceptTcpClient();
                
                
                Console.WriteLine("Connected!");

                data = null;

                // Get a stream object for reading and writing
                NetworkStream stream = client.GetStream();

                int i;

                data = "Hello, world!";
                byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
                
                StreamWriter writer = new StreamWriter(stream);
                Console.WriteLine(writer.Encoding);

                writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
                writer.WriteLine("Upgrade: WebSocket");
                writer.WriteLine("Connection: Upgrade");
                writer.WriteLine("WebSocket-Origin: http://localhost:80");
                writer.WriteLine("WebSocket-Location: ws://localhost:13001/websession");
                writer.WriteLine("Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+x0o=");
                writer.WriteLine("");

                writer.WriteLine("Hello world!");
                writer.WriteLineAsync("Hello asynchronous world!");

                // Send back a response.
                //stream.Write(msg, 0, msg.Length);
                //Console.WriteLine("Sent: {0}", data);
                

                // Loop to receive all the data sent by the client. 
                //while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                //{
                //    // Translate data bytes to a ASCII string.
                //    data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
                //    Console.WriteLine("Received: {0}", data);

                //    // Process the data sent by the client.
                //    data = data.ToUpper();

                //    byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);

                //    // Send back a response.
                //    stream.Write(msg, 0, msg.Length);
                //    Console.WriteLine("Sent: {0}", data);
                //}

                
                // Shutdown and end connection
                writer.Flush();
                
                
                client.Close();
                break;
            }
        }
        catch (SocketException e)
        {
            Console.WriteLine("SocketException: {0}", e);
        }
        finally
        {
            // Stop listening for new clients.
            sserver.Stop();
        }

        Console.WriteLine("\nHit enter to continue...");
        Console.Read();

        _dbServer = IPAddress.Parse("192.168.1.199");
        _dbName = "CriminalTracker";
        _criminalTrackerDB = new SqlConnection("user id=" + _username + ";" +
                                                "password=" + _password + ";" +
                                                "server=" + _dbServer + ";" +
                                                "Trusted_Connection=yes;" +
                                                "database=" + _dbName + ";" +
                                                "connection timeout=30");


        
        TraceListener listener = new TraceListener();
        new Thread(listener.listen).Start();
        //TraceHandler handler = new TraceHandler(listener, "192.168.1.199", "CriminalTracker");

        //Console.WriteLine("MAIN -- Subscribing TraceHandler to TraceListener");
        //handler.Subscribe(listener);

        using (var source = listener)
        {
            Console.WriteLine("connecting to StreamInsight server");
            using (var server = Server.Create(STREAMSERVER))
            {
                Console.WriteLine("creating StreamInsight application");
                var application = server.CreateApplication(STREAMAPP);

                Console.WriteLine("creating CEP stream from observable (TraceListener)");
                var stream = source.ToPointStream(application,
                                        e => PointEvent.CreateInsert(DateTime.Now, new Payload { _trace = e }),
                                        AdvanceTimeSettings.StrictlyIncreasingStartTime,
                                        "Observable Stream");
                var testQuery = from e in stream
                            //where e._trace.lon <= -122.191286
                            select e._trace;

                //var restrictedQuery = from e in stream
                //                      select new { locationID = myFunctions.restricted(e._trace.lat, e._trace.lon) };

                //var observableOutput = query.ToPointObservable()
                //                    .Where(e => e.Payload._trace.lon <= -122.191286)
                //                    .Select(e => e.Payload._trace);

                foreach (var trace in testQuery.ToEnumerable())
                {
                    //Console.WriteLine("Trace: " + trace.ToString());
                    //int locationID = myFunctions.restricted(trace.lat, trace.lon);
                    
                    _criminalTrackerDB.Open();
                    var db = new criminal_tracker_590.CriminalTrackerEntities();
                    string point = "POINT(" + trace.lon + " " + trace.lat + ")";
                    var myLoc = DbGeography.FromText(point);
                    criminal_tracker_590.history_spatial history = new criminal_tracker_590.history_spatial
                    {
                        date_time = trace.time,
                        offenderID = trace.offender,
                        location = myLoc
                    };
                    db.history_spatial.Add(history);

                    //OIDs oids = new OIDs(trace.offender);
                    //Location loc = new Location(trace.time, trace.lat, trace.lon);

                    List<Location> locationList = new List<Location>();
                    locationList.Add(new Location(trace.time, trace.lat, trace.lon));

                    //Locations locs = new Locations(locationList);

                    Offender off = new Offender(trace.offender, locationList);
                    

                    List<Offender> offenderList = new List<Offender>();
                    offenderList.Add(off);
                    List<Offenders> offenders = new List<Offenders>();
                    offenders.Add(new Offenders(offenderList));
                    OffenderJSON offenderContract= new OffenderJSON(offenders);

                    String json = JsonConvert.SerializeObject(offenderContract);
                    Console.WriteLine("front-end-json: " + json);
                    Console.WriteLine();

                    ////Location[] locations = new Location[2];
                    //List<Location> locations = new List<Location>();

                    //Location temp = new Location(trace.time, trace.lat, trace.lon);
                    //Location temp2 = new Location(trace.time, trace.lat, trace.lon);

                    //locations.Add(temp);
                    //locations.Add(temp2);

                    //Offender testOffender = new Offender(trace.offender, locations);
                    ////Offender[] testOffenderArray = new Offender[1];
                    //List<Offender> testOffenderArray = new List<Offender>();
                    //testOffenderArray.Add(testOffender);

                    //Offenders offenders = new Offenders(testOffenderArray);


                    //String json = JsonConvert.SerializeObject(offenders);

                    //Console.WriteLine("front-end-json: " + json);
                    //Console.WriteLine();

                    ////JSON output to UI
                    //JsonObject recentTrace = new JsonObject();
                    ////KeyValuePair<string, KeyValuePair<string, string>[]> offenders = new KeyValuePair<OFFENDER,KeyValuePair<string,string>[]>;
                    //JsonObject guts = new JsonObject();
                    ////guts.Add(TIMESTAMP, stamp);
                    //guts.Add(TIMESTAMP, trace.time);
                    //guts.Add(LAT, trace.lat);
                    //guts.Add(LONG, trace.lon);

                    //JsonObject[] locats = new JsonObject[1];

                    //JsonArray locatsb
                    //    = new JsonArray(locats);


                    //locats[0] = guts;

                    //JsonObject location = new JsonObject();
                   // location.Add(LOCATION, guts); // <- this isn't an array


                    int zoneID = myFunctions.restrictedSpatial(trace, 100);

                    // restricted zone 
                    if (zoneID != -1)
                    {
                        Console.WriteLine("***Restricted Zone Violation for LocationID: " + zoneID);
                        //Event alert = new Event(1, trace.offender, trace.lon, trace.lat);
                        //alert.eventLogInsert();
                        
                        //SqlCommand writeToEventLog = alert.eventLogInsert();
                        //writeToEventLog.Connection = _criminalTrackerDB;
                        //writeToEventLog.ExecuteNonQuery();

                        criminal_tracker_590.eventLog_spatial eventEntry = new criminal_tracker_590.eventLog_spatial 
                        {
                            date_time = trace.time,
                            eventTypeID = 1,
                            eventLocation = myLoc,
                            locationID = zoneID,
                            offenderID = trace.offender
                        };
                        db.eventLog_spatial.Add(eventEntry);

                        List<OID_Involved> oidList = new List<OID_Involved>();
                        oidList.Add(new OID_Involved(trace.offender, trace.lat, trace.lon));
                        Alerts alerts = new Alerts(1,
                                                    trace.time,
                                                    "Restricted Zone Violation",
                                                    "test message",
                                                    "test status",
                                                    "test event",
                                                    zoneID,
                                                    oidList);
                        List<Alerts> alertList = new List<Alerts>();
                        alertList.Add(alerts);
                        AlertsJSON alertContract = new AlertsJSON(alertList);
                        
                        String jsonalert = JsonConvert.SerializeObject(alertContract);
                        Console.WriteLine("alert-json: " + jsonalert);
                        Console.WriteLine();

                                                    

                        clientFeed pusher = new clientFeed();
                        //clientFeed pusher = new clientFeed();

                        //bool pushResult = pusher.publish(new AlertType(trace.offender, trace.time, trace.lon, trace.lat, 0.00));

                        
                        //serverHub.Invoke("Publish", new AlertType(trace.offender, trace.time, trace.lon, trace.lat, 0.00)).Wait();
                        
                    }

                    List<int> proxOffenders = myFunctions.offenderProximity(trace, 25, 5.0);
                   
                    foreach (var c in proxOffenders)
                    {
                        if (c != -1)
                        {
                            Console.WriteLine("!!!!!! offender proximity alert");
                            criminal_tracker_590.eventLog_spatial eventEntry = new criminal_tracker_590.eventLog_spatial
                            {
                                date_time = trace.time,
                                eventTypeID = 3,
                                eventLocation = myLoc,
                                partnerOffenderID = c,
                                offenderID = trace.offender
                            };
                            db.eventLog_spatial.Add(eventEntry);
                        }
                    }

                    
                    
                    db.SaveChanges();
                    
                    SqlCommand writeToHistory = trace.historyTableInsert();
                    writeToHistory.Connection = _criminalTrackerDB;
                    writeToHistory.ExecuteNonQuery();
                    _criminalTrackerDB.Close();
   
                }

                //foreach (var locationID in restrictedQuery.ToEnumerable())
                //{
                //    if (locationID.ToString() != "-1")
                //    {
                //        Console.WriteLine("***Restricted Zone Violation for OffenderID: " + locationID);
                //    }

                //    //_criminalTrackerDB.Open();
                //    //SqlCommand writeToHistory = payload._trace.historyTableInsert();
                //    //writeToHistory.Connection = _criminalTrackerDB;
                //    //writeToHistory.ExecuteNonQuery();
                //    //_criminalTrackerDB.Close();
                //}

                //Console.ReadLine();
                //using (query.ToObservable().Subscribe(Console.WriteLine))
                //{
                //    Console.WriteLine("Started query.");
                //    Console.ReadLine();
                //    Console.WriteLine("Stopping query...");
                //}
                //Console.WriteLine("Stopped query.");
            }
            Console.ReadLine();
            Console.WriteLine("Stopping observable source...");
            source.OnCompleted();     
            Console.WriteLine("Stopped observable source.");
        }
    }
}

public class myFunctions
{
    //public static int restricted(double lat, double lon)
    //{
    //    //Console.WriteLine("MYFUNCTIONS -- ENTERED RESTRICTED()");
    //    decimal latdec = (decimal)lat;
    //    decimal londec = (decimal)lon;

    //    var db = new criminal_tracker_590.CriminalTrackerEntities();

    //    var query = from x in db.locations
    //                where latdec < x.latitude1
    //                && latdec > x.latitude2
    //                && londec > x.longitude1
    //                && londec < x.longitude2
    //                select x.locationID;

    //    int locationID = -1;

    //    foreach (var c in query)
    //    {
    //        locationID = c;
    //    }

    //    //Console.WriteLine("MYFUNCTIONS -- OOOOOOOOOOOOOOOOOOOOOOOOO" + query.ToString());
    //    return locationID;
    //}

    public static int restrictedSpatial(Trace trace, int range)
    {
        var db = new criminal_tracker_590.CriminalTrackerEntities();
        string point = "POINT(" + trace.lon + " " + trace.lat + ")";

        var myLoc = DbGeography.FromText(point);
        //var myLoc = DbGeography.FromText("POINT(174.65964692831042 -36.724981172899945)");

        var myLoc2 = myLoc.Buffer(range);
              
        //var point = geography :: Point (-36.724981172899945,174.65964692831042, 4326);

        var query = from x in db.location_spatial
                    where myLoc2.Intersects(x.region)
                    select x.locationID;
        int locationID = -1;
        foreach (var c in query)
        {
            locationID = c;
        }

        return locationID;
    }

    public static int[] nearestRestricteds(Trace trace, int range, int quantity)
    {
        int[] rv = new int[quantity];
        for (int i = 0; i < quantity; ++i)
        {
            rv[i] = -1;
        }
        if (restrictedSpatial(trace, range) != -1)
        {
            var db = new criminal_tracker_590.CriminalTrackerEntities();

            string point = "POINT(" + trace.lon + " " + trace.lat + ")";

            var myLoc = DbGeography.FromText(point);

            var query = (from x in db.location_spatial
                         orderby x.region.Distance(myLoc)
                         // where myLoc2.Intersects(x.region)
                         select x.locationID).Take(quantity);

            int index = 0;
            foreach (var c in query)
            {
                rv[index] = c;

                ++index;
            }
        }

        return rv;
    }

    public static List<int> offenderProximity(Trace trace, int range, double delay)
    {
        var db = new criminal_tracker_590.CriminalTrackerEntities();
        string point = "POINT(" + trace.lon + " " + trace.lat + ")";

        var myLoc = DbGeography.FromText(point).Buffer(range);
        var currentTime = trace.time;
        var offender = trace.offender;
        List<int> offenders = new List<int>();

        DateTime offset = new DateTime();
        offset = currentTime.Subtract(TimeSpan.FromSeconds(delay));

        var query = from x in db.history_spatial
                    where offender != x.offenderID
                    && x.date_time > offset
                    && myLoc.Intersects(x.location)
                    select  x.offenderID;
        int offenderID = -1;
        foreach (var c in query.Distinct<int>())
        {
            offenderID = c;

            offenders.Add(offenderID);
        }

        //List<int> distinctOffenders = new List<int>();
        //distinctOffenders =
        //    offenders.Distinct<int>().ToList<int>();

        //return distinctOffenders;
        return offenders;
    }
    
}

//public class MyConnection : PersistentConnection
//{
//}

//class testWorker
//{
//    private HttpListenerContext context;

//    public testWorker(HttpListenerContext context)
//    {
//        this.context = context;
//    }

//    public void ProcessRequest()
//    {
//        string msg = context.Request.HttpMethod + " " + context.Request.Url;
//        Console.WriteLine(msg);
//    }
//}